package com.java.base.exercise.threadcommunication;

import java.util.Objects;
import java.util.concurrent.locks.LockSupport;

/**
 * @Author RenPu
 * @Date 2023/3/6 16:08
 * @Version 1.0
 * @Description: 线程通信的案例模拟以及特性的总结
 **/
public class ThreadTest1 {

    public static Object bus=null;


    /**=============suspend和resume的线程通信案例，以及优缺点的总结============================================================**/


    /**
     * suspend挂起当前线程  resume：唤醒线程
     */
    public static void  suspendResumeTest() {

        Thread thread = new Thread(() -> {
            while (Objects.isNull(bus)) {
                System.out.println("开始在站牌等公交，公交还没来则进行等待公交到站。。。。。。");
                Thread.currentThread().suspend();
            }
            System.out.println("公交车来了，乘坐公交开心下班回家了");
        });
        thread.start();
        try {
            Thread.sleep(3000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        bus=new Object();
        thread.resume();
    }


    /**
     *     死锁案例的模拟1，
     *     suppend不会主动释放锁，导致resume一直获取不到锁，
     *     导致的死锁状况
     */
    public void  suspendResumeDeadLock()  {
        Thread thread = new Thread(() -> {
            while (Objects.isNull(bus)) {
                System.out.println("开始在站牌等公交，公交还没来则进行等待公交到站。。。。。。");
                synchronized (this){
                    Thread.currentThread().suspend();
                }
            }
            System.out.println("公交车来了，乘坐公交开心下班回家了");
        });
        thread.start();
        try {
            Thread.sleep(3000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        bus=new Object();
        synchronized (this){
            thread.resume();
        }

    }

    /**
     * 死锁案例的模拟2
     *  suspend必须在resume之前进行执行，这个顺序一定要严格保证
     *  否则也会产成死锁
     */
    public void  suspendResumeDeadLock2()  {

        Thread thread = new Thread(() -> {
            while (Objects.isNull(bus)) {
                try {
                    Thread.sleep(5000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("开始在站牌等公交，公交还没来则进行等待公交到站。。。。。。");
                Thread.currentThread().suspend();
            }
            System.out.println("公交车来了，乘坐公交开心下班回家了");
        });
        thread.start();
        try {
            Thread.sleep(3000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        bus=new Object();
        thread.resume();

    }
  /**============================
   * suppend和resume总结，俩者都不会主动释放锁，执行顺序一定要保证先进行挂起，
   * 然后进行唤醒，否则会产生死锁的发生
   * ============================================**/


    /**=============wait和notifyAll的线程通信案例，以及优缺点的总结============================================================**/





    /**
     * wait和notifyAll要结合同步代码块，进行执行
     * 原因是：通过俩个来实现线程通信得方式，是结合着对象的监听模式，来实现所以没有同步代码块配合就会抛出异常
     *  wait当获取到锁之后，会将线程进行设为等待状态，并且释放锁资源，同时加入到这个对象的等待集里
     *  notifyAll从而可以获取到释放的锁。进行对其进行状态的变更
     *
     *  错误代码案例：
     *  new Thread(()->{
     *      if(...){
     *
     *      }
     *
     *  })
     *
     *
     *  
     * 正确代码案例：
     * new Thread(()->{
     *     while(.....){
     *
     *     }
     * })
     *
     *
     *
     *
     */
    public void waitNotifyTest(){
        new Thread(() -> {
         while (Objects.isNull(bus)){
             synchronized (this){
                 try {
                     System.out.println("开始在站牌等公交，公交还没来则进行等待公交到站。。。。。。");
                     this.wait();
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
             }
         }
           System.out.println("公交车来了，乘坐公交开心下班回家了。。。。。。");
       }).start();

       try {
           Thread.sleep(3000L);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
        bus=new Object();
       synchronized (this){
           this.notifyAll();
           System.out.println("通知消费者。。。。。");
       }
   }


    /**
     *  wait和notfiyAll虽然可以释放锁资源，在死锁方面做出了优化
     *  但是在顺序方面，还是需要严格的遵守的，必须先执行wait，然后在进行执行notifyAll
     *  否则的话也同时会产生死锁
     */
    public void waitNotifyDeadLock(){
        new Thread(() -> {
            while (Objects.isNull(bus)){
                try {
                    Thread.sleep(5000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (this){
                    try {
                        System.out.println("开始在站牌等公交，公交还没来则进行等待公交到站。。。。。。");
                        this.wait();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
            System.out.println("公交车来了，乘坐公交开心下班回家了。。。。。。");
        }).start();

        try {
            Thread.sleep(3000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        bus=new Object();
        synchronized (this){
            this.notifyAll();
            System.out.println("通知消费者。。。。。");
        }
    }

    /**============================
     *  wait和notfiyAll虽然可以释放锁资源，在死锁方面做出了优化
     *  但是在顺序方面，还是需要严格的遵守的，必须先执行wait，然后在进行执行notifyAll
     *  否则的话也同时会产生死锁
     * ============================================**/


    /**
     * park和unpark，线程方式的通信
     * 是一张颁发标志位的形式，进行线程之间的通信，对编写的顺序没有任何要求
     * unpark就是给颁发许可证，park就是获取许可证
     * 例如：先执行了unpark，就代表已经颁发了令牌，此时park就可以正常获取到令牌执行接下来的业务逻辑
     * 假如：park先执行了，由于获取不到颁发的令牌，此时就得等待unpark进行令牌的颁发
     */
    public void parkUnpark(){

        Thread thread = new Thread(() -> {
            while (Objects.isNull(bus)) {
                try {
                    System.out.println("开始在站牌等公交，公交还没来则进行等待公交到站。。。。。。");
                    LockSupport.park();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            System.out.println("公交车来了，乘坐公交开心下班回家了。。。。。。");
        });
        thread.start();
        try {
            Thread.sleep(3000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        bus=new Object();
            LockSupport.unpark(thread);
            System.out.println("通知消费者。。。。。");

    }



    /**
     * park和unpark，产生死锁的模拟案例
     * 由于此设计不是采用监听的方式，进行设计的从而导致锁是不能自动进行释放的、
     * 从而会有出现死锁的风险需要编写时注意
     */
    public void parkUnparkDeadLock(){

        Thread thread = new Thread(() -> {
            while (Objects.isNull(bus)) {
                    synchronized (this) {
                        System.out.println("开始在站牌等公交，公交还没来则进行等待公交到站。。。。。。");
                        LockSupport.park();
                    }
            }
            System.out.println("公交车来了，乘坐公交开心下班回家了。。。。。。");
        });
        thread.start();

        try {
            Thread.sleep(3000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        bus=new Object();
        synchronized (this) {
            LockSupport.unpark(thread);
        }
        System.out.println("通知消费者。。。。。");

    }


    /**
     * 伪唤醒
     * 在线程中进行条件判断时，禁止使用if（）进行判断
     * 官方表示此种写法，会产生伪唤醒，就是说不是由，notfiyAll,unpark进行唤醒的，而是由硬件更底层的导致唤醒的
     * 在线程中条件判断推荐使用while(),进行条件判断，if只进行了一次条件判断，从而不能避免伪唤醒，而while（）会多次进行
     * 对条件进行判断，从而可以避免由于更底层或者硬件层面导致的伪唤醒问题
     * @param args
     */





    public static void main(String[] args) {
           // suspendResumeTest();
       //new ThreadTest1().suspendResumeDeadLock();
       // new ThreadTest1().suspendResumeDeadLock2();
        //new ThreadTest1().waitNotifyTest();
        //new ThreadTest1().waitNotifyDeadLock();
       // new ThreadTest1().parkUnpark();
        new ThreadTest1().parkUnparkDeadLock();

        Thread thread = new Thread();
        thread.interrupt();
        thread.stop();



    }


}
